import { ServiceDict } from './../../app.module';
import { Injectable } from "@nestjs/common";
import { globalError } from "../interceptors/httpResult.interceptor";
import { getNestApplication } from "src/main";

import { List } from "./../../controllers/list/list.entity";

let modelMap = {
    List
};

type FindMethod = 'findById' | 'findOne' | 'find';

interface ModelPopulateOptions {
    /** space delimited path(s) to populate */
    path: string;
    /** optional fields to select */
    select?: any;
    /** optional query conditions to match */
    match?: any;
    /** optional name of the model to use for population */
    model?: string;
    /** optional query options like sort, limit, etc */
    options?: any;
    /** deep populate */
    populate?: ModelPopulateOptions | ModelPopulateOptions[];
}

@Injectable()
export class RestfulService {
    /**
     * 默认使用post方法获取
     * model代表需要获取的模型
     * findMethod表示查找方式：（默认）
     * cond表示查找的条件，支持多级查询，比如：
     * cond = {
     *      company: {
     *          restDeepCond: {
     *              model: 'Company',
     *              cond: '.*中金.*'
     *          }
     *      }
     * }
     * pageSize和pageNum必须为number类型
     * select表示是否只选择其中某些字段
     * populate 表示展开哪些字段
     * sort表示排序
     * withCount表示是否附带字段：该cond下一共有多少模型
     */
    async getModel(body: {
        model: string;
        findMethod?: FindMethod;
        cond: any;
        pageSize?: number;
        pageNum?: number;
        select?: string;
        populate?: string | ModelPopulateOptions | ModelPopulateOptions[];
        sort?: any;
        withCount?: boolean;
    }) {
        let Model = modelMap[body.model];
        if (!Model) {
            globalError('模型未注册');
        }
        let findMethod: string =
            body.findMethod ||
            (typeof body.cond == 'string' ? 'findById' : 'find');
        if (findMethod == 'find') {
            findMethod = 'ybFind';
        } else if (findMethod == 'findOne') {
            findMethod = 'ybFindOne';
        }

        await this.processCond(body.cond);

        let query = Model[findMethod](body.cond);
        if (
            'number' == typeof body.pageSize &&
            'number' == typeof body.pageNum
        ) {
            query.skip(body.pageSize * body.pageNum);
            query.limit(body.pageSize);
        }
        if (body.select) {
            query.select(body.select);
        }
        if (body.populate) {
            query.populate(body.populate);
        }
        if (body.sort) {
            query.sort(body.sort);
        }
        let result = await query.exec();
        // withCount 是否统计数量
        let count = 0;
        if (body.withCount) {
            count = await Model.ybCount(body.cond).exec();
        }

        return {
            result,
            count,
        };
    }

    /**
     * 默认使用put方法获取
     * model代表需要获取的模型
     * cond表示查找的条件 若cond = string则使用findById，否则findOne
     * updates object对象，表示更新哪些地方
     */
    async createOrUpdateModel(body: {
        model: string;
        findMethod?: FindMethod;
        cond: any;
        updates: any;
        afterSave?: {
            service: string;
            method: string;
        };
    }) {
        let Model = modelMap[body.model];
        if (!Model) {
            globalError('模型未注册');
        }

        let cond = body.cond;

        let result: any;
        if (!cond) {
            // 创建一个新的对象
            result = new Model();
        } else {
            let findMethod: string =
                body.findMethod ||
                ('string' == typeof cond ? 'findById' : 'findOne');
            if (findMethod == 'findOne') {
                findMethod = 'ybFindOne';
            }
            if (findMethod == 'find') {
                throw '不支持find方法，只支持findOne和findById';
            }
            // 寻找模型
            result = await Model[findMethod](cond);
            if (!result) {
                throw '没有找到条件对应的模型';
            }
        }

        // 更新
        let updates = body.updates;
        delete updates._id;

        for (let key in updates) {
            result[key] = updates[key];
        }
        await result.save();

        // 保存之后有一个回调方法可以做一些其他逻辑变动
        if (body.afterSave) {
            let { service, method } = body.afterSave;
            let serviceInstance = getNestApplication().get(
                ServiceDict[service],
            );
            if (serviceInstance[method]) {
                await serviceInstance[method](result, body);
            }
        }

        return result;
    }

    /**
     * 统计数据
     */
    async condCount(body: { model: string; conds: any[] }) {
        let Model = modelMap[body.model];
        if (!Model) {
            globalError('模型未注册');
        }
        let counts = await Promise.all(
            body.conds.map(cond => Model.count(cond)),
        );
        return {
            counts,
        };
    }

    private async processCond(cond: any) {
        if (!cond) return;
        // cond 可以为对象或者数组，所以key可以是number或者string
        for (let key in cond) {
            let val = cond[key];
            if (key == '$deep') {
                await this.processCond(val.cond);
                let TModel = modelMap[val.model];
                let result = await TModel.find(val.cond);
                delete cond[key];
                cond.$in = result.map(it => it.id);
            } else if (typeof val == 'object') {
                await this.processCond(val);
            }
        }
    }
}
